package core

import (
	"freechat-im/common"
	"freechat-im/config"
	"freechat-im/util"
	"gitee.com/goddesszz/gocommon/myutil"
	"github.com/sirupsen/logrus"
	"strconv"
	"time"
)

/*

 */
/**
 * 在线用户登记表
 */
//const REDIS_KEY_ONLINE_PREFIX = "chat-online:"

// REDIS_KEY_USER_INDEX_PREFIX
// ? 如果节点挂了，但是redis中user没清。 解决方案：应该能容忍这种情况，当节点挂掉时，user客户端会进行重连，此时会连接到其他节点，会继续登记注册表。
const REDIS_KEY_USER_INDEX_PREFIX = "online-user-index-"

type OnlineUserRegister interface {
	// CheckIn 用户连接成功时登记
	CheckIn(userId string)
	// CheckOut 用户断开连接时退出登记
	CheckOut(userId string)
	// IsOnline 用户是否在线
	IsOnline(userId string) bool
	// GetUserNode 获取用户登录的机器节点名称
	GetUserNode(userId string) string
}

// 单机模式的user上线注册
type StandaloneOnlineUserRegister struct {
	context *ChContext
}

func NewStandaloneOnlineUserRegister(context *ChContext) *StandaloneOnlineUserRegister {
	return &StandaloneOnlineUserRegister{
		context: context,
	}
}

func (StandaloneOnlineUserRegister) CheckIn(userId string) {
	go func() {
		//记录用户最后一次的连接时间
		err := config.GetRedisClient().HSet(config.GetRedisContext(), common.REDIS_KEY_USER_LastTime_INDEX_PREFIX+index(userId), userId, time.Now().UnixMilli()).Err()
		if err != nil {
			logrus.Error("Redis 保存数据时出错. err = ", err.Error())
		}
		logrus.Debug("user 连接后 CheckIn success. ")
	}()
}
func (StandaloneOnlineUserRegister) CheckOut(userId string) {
}
func (register StandaloneOnlineUserRegister) IsOnline(userId string) bool {
	return register.context.LocalOnline(userId)
}
func (StandaloneOnlineUserRegister) GetUserNode(userId string) string {
	return common.NodeName
}

// 集群模式的user上线注册
type ClusterOnlineUserRegister struct {
	context *ChContext
}

func NewClusterOnlineUserRegister(context *ChContext) *ClusterOnlineUserRegister {
	return &ClusterOnlineUserRegister{
		context: context,
	}
}

func (ClusterOnlineUserRegister) CheckIn(userId string) {
	go func() {
		//config.GetRedisClient().Set(config.GetRedisContext(), REDIS_KEY_ONLINE_PREFIX+model.NodeName, userId,0).Err()
		err := config.GetRedisClient().HSet(config.GetRedisContext(), REDIS_KEY_USER_INDEX_PREFIX+index(userId), userId, common.NodeName).Err()
		if err != nil {
			logrus.Error("Redis 保存数据时出错. err = ", err.Error())
		}

		//记录用户最后一次的连接时间
		err = config.GetRedisClient().HSet(config.GetRedisContext(), common.REDIS_KEY_USER_LastTime_INDEX_PREFIX+index(userId), userId, time.Now().UnixMilli()).Err()
		if err != nil {
			logrus.Error("Redis 保存数据时出错. err = ", err.Error())
		}
		logrus.Debug("user 连接后 CheckIn success. ")
	}()
}

func (ClusterOnlineUserRegister) CheckOut(userId string) {
	go func() {
		config.GetRedisClient().HDel(config.GetRedisContext(), REDIS_KEY_USER_INDEX_PREFIX+index(userId), userId)
		logrus.Debug("user 连接后 CheckOut success. ")
	}()
}

func (register ClusterOnlineUserRegister) IsOnline(userId string) bool {
	//先判断是不是已连接到本节点，再判断是不是连接到其他节点
	var online = register.context.LocalOnline(userId)
	if !online {
		nodeName := register.GetUserNode(userId)
		//有节点值，且节点是活着的，说明已经连接注册了
		online = (nodeName != "") &&
			util.ZkUtil.Exists(common.NodeActivePath+"/"+nodeName)
	}
	return online
}

func (ClusterOnlineUserRegister) GetUserNode(userId string) string {
	nodeName, _ := config.GetRedisClient().HGet(config.GetRedisContext(), REDIS_KEY_USER_INDEX_PREFIX+index(userId), userId).Result()
	return nodeName
}

func index(userId string) string {
	return strconv.Itoa(myutil.HashCode(userId) % 10)
}
